<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

abstract class BaseModel extends Model
{
    use SoftDeletes;

    protected $guarded = [];

    // 启用时间戳
    public $timestamps = true;

    protected $dateFormat = 'U';

    protected $casts = [
        'created_at' => 'date:Y-m-d H:i:s',
        'updated_at' => 'date:Y-m-d H:i:s',
        'deleted_at' => 'date:Y-m-d H:i:s',
    ];

    // 分表配置
    protected $partitionEnabled = false;           // 是否启用分表
    protected $partitionType = 'year';             // 分表类型: year, month, day, quarter, hash
    protected $partitionHashCount = 10;            // hash分表数量（当type为hash时使用）

    /**
     * 时区错误坑点，重写方法
     * @param \DateTimeInterface $date
     * @return string
     */
    protected function serializeDate(\DateTimeInterface $date)
    {
        return $date->format('Y-m-d H:i:s');
    }

    protected static function boot()
    {
        parent::boot();

        static::creating(function ($model) {
            if (!isset($model->attributes['created_at'])) {
                $model->attributes['created_at'] = time();
            }
            if (!isset($model->attributes['updated_at'])) {
                $model->attributes['updated_at'] = time();
            }
        });

        static::updating(function ($model) {
            $model->attributes['updated_at'] = time();
        });
    }

    /**
     * 检查是否启用分表
     */
    protected function isPartitionEnabled(): bool
    {
        return $this->partitionEnabled;
    }

    /**
     * 计算分表后缀
     * @param mixed $value 分表依据值
     * @return string
     */
    protected function calculatePartitionSuffix($value): string
    {
        switch ($this->partitionType) {
            case 'year':
                return $this->getYearSuffix($value);
            case 'month':
                return $this->getMonthSuffix($value);
            case 'day':
                return $this->getDaySuffix($value);
            case 'quarter':
                return $this->getQuarterSuffix($value);
            case 'hash':
                return $this->getHashSuffix($value);
            default:
                return $this->getYearSuffix($value);
        }
    }

    /**
     * 按年分表后缀
     */
    protected function getYearSuffix($value): string
    {
        if (is_numeric($value)) {
            return date('Y', $value);
        }
        return date('Y', strtotime($value));
    }

    /**
     * 按月分表后缀
     */
    protected function getMonthSuffix($value): string
    {
        if (is_numeric($value)) {
            return date('Ym', $value);
        }
        return date('Ym', strtotime($value));
    }

    /**
     * 按日分表后缀
     */
    protected function getDaySuffix($value): string
    {
        if (is_numeric($value)) {
            return date('Ymd', $value);
        }
        return date('Ymd', strtotime($value));
    }

    /**
     * 按季度分表后缀
     */
    protected function getQuarterSuffix($value): string
    {
        $timestamp = is_numeric($value) ? $value : strtotime($value);
        $month = date('n', $timestamp);
        $quarter = ceil($month / 3);
        return date('Y', $timestamp) . 'Q' . $quarter;
    }

    /**
     * 按Hash分表后缀
     */
    protected function getHashSuffix($value): string
    {
        // 支持数字ID或字符串的hash分表
        if (is_numeric($value)) {
            $hashValue = $value % $this->partitionHashCount;
        } else {
            $hashValue = crc32($value) % $this->partitionHashCount;
        }
        return str_pad(abs($hashValue), 2, '0', STR_PAD_LEFT);
    }

    /**
     * 获取分表名
     * @param string $partitionValue 分表依据值
     * @return string
     */
    protected function getPartitionTableName($partitionValue): string
    {
        if (!$this->isPartitionEnabled()) {
            return $this->getTable();
        }

        $suffix = $this->calculatePartitionSuffix($partitionValue);
        $baseTable = $this->getOriginalTable();
        return "{$baseTable}_{$suffix}";
    }

    /**
     * 获取原始表名（不带分表后缀）
     */
    protected function getOriginalTable(): string
    {
        // 如果已经设置了table属性，使用它
        if (isset($this->table) && $this->table) {
            return $this->table;
        }

        // 否则根据类名生成表名
        $className = class_basename($this);
        return strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $className)) . 's';
    }

    /**
     * 设置分表
     * @param mixed $partitionValue 分表依据值（可以是字符串、数字、数组或null）
     */
    protected function setTableForPartition($partitionValue = null): void
    {
        if (!$this->isPartitionEnabled()) {
            return;
        }

        $tableName = $this->getPartitionTableName($partitionValue);

        // 确保表存在
        if (!$this->tableExists($tableName)) {
            $this->createPartitionTable($tableName);
        }

        $this->setTable($tableName);
    }

    /**
     * 检查表是否存在
     */
    protected function tableExists(string $tableName): bool
    {
        return Schema::hasTable($tableName);
    }

    /**
     * 创建分表
     */
    protected function createPartitionTable(string $tableName): bool
    {
        if ($this->tableExists($tableName)) {
            return true;
        }

        try {
            $tableNamePrefix = env('DB_PREFIX', '');
            $originalTable = $this->getOriginalTable();

            // 获取原始表结构
            if (!$this->tableExists($originalTable)) {
                throw new \Exception("Original table {$originalTable} does not exist");
            }

            // 复制表结构
            DB::statement("CREATE TABLE `{$tableNamePrefix}{$tableName}` LIKE `{$tableNamePrefix}{$originalTable}`");

            Log::info("Created partition table: {$tableName}");
            return true;
        } catch (\Exception $e) {
            Log::error("Failed to create partition table: {$tableName}", ['error' => $e->getMessage()]);
            return false;
        }
    }

    /**
     * 构建查询条件
     * @param array $params 查询参数
     * @return \Illuminate\Database\Eloquent\Builder
     */
    protected function buildQuery(array $params = [])
    {
        return $this->defaultQuery($params);
    }

    /**
     * 获取表字段
     * @return array
     */
    abstract protected function columns(): array;

    /**
     * 默认查询条件
     * @param array $params 查询参数
     * @return \Illuminate\Database\Eloquent\Builder
     */
    protected function defaultQuery(array $params = []): \Illuminate\Database\Eloquent\Builder
    {
        $query = $this->query();

        if (!empty($params)) {
            $blacklist = ['page', 'page_size', 'sort', 'order'];
            foreach ($params as $column => $value) {
                if (in_array($column, $this->columns()) && (!empty($value) || $value === 0 || $value === '0') && !in_array($column, $blacklist)) {
                    $query->where($column, $value);
                }
            }
        }
        // 优先使用传入的排序参数
        if (!empty($params['sort'])) {
            $query->orderBy($params['sort'], $params['order'] ?? 'desc');
        } else {
            // 默认id desc
            $query->orderBy('id', 'desc');
        }
        // 分页
        if (!empty($params['page']) && !empty($params['page_size'])) {
            $query->forPage($params['page'], $params['page_size']);
        }
        return $query;
    }

    /**
     * 过滤数据
     * @param array $data
     * @return array
     */
    protected function filterData(array $data): array
    {
        return array_intersect_key($data, array_flip($this->columns()));
    }

    /**
     * 获取列表（支持分表）
     * @param array $params
     * @param array $fields 查询字段
     * @param array $with 预加载关联
     * @param mixed $partitionValue 分表依据值
     * @return array
     */
    public function list(array $params = [], array $fields = ['*'], array $with = [], $partitionValue = null): array
    {
        // 设置分表 - 优先使用传入的分表依据
        if ($this->isPartitionEnabled() && $partitionValue) {
            $this->setTableForPartition($partitionValue);
        }

        $query = $this->buildQuery($params)->select($fields);

        if (!empty($with)) {
            $query->with($with);
        }

        $paginator = $query->paginate($params['page_size'] ?? 15);

        return [
            'items' => $paginator->items(),
            'total' => $paginator->total()
        ];
    }

    /**
     * 根据条件获取单条记录（支持分表）
     * @param array $params
     * @param array $fields 查询字段
     * @param array $with 预加载关联
     * @param mixed $partitionValue 分表依据值
     * @return array
     */
    public function take(array $params = [], array $fields = ['*'], array $with = [], $partitionValue = null): array
    {
        if ($this->isPartitionEnabled() && $partitionValue) {
            $this->setTableForPartition($partitionValue);
        }

        $query = $this->buildQuery($params)->select($fields);

        if (!empty($with)) {
            $query->with($with);
        }

        $result = $query->first();
        return $result ? $result->toArray() : [];
    }

    /**
     * 自定义条件查询（支持分表）
     * @param array $params
     * @param array $fields 查询字段
     * @param array $with 预加载关联
     * @param mixed $partitionValue 分表依据值
     * @return array
     */
    public function search(array $params = [], array $fields = ['*'], array $with = [], $partitionValue = null): array
    {
        // 设置分表 - 优先使用传入的分表依据
        if ($this->isPartitionEnabled() && $partitionValue) {
            $this->setTableForPartition($partitionValue);
        }

        $query = $this->buildQuery($params)->select($fields);

        if (!empty($with)) {
            $query->with($with);
        }

        return $query->get()->toArray();
    }

    /**
     * 创建记录（支持外部传入分表依据）
     * @param array $data
     * @param mixed $partitionValue 可选，外部传入的分表依据值
     * @return int
     */
    public function create(array $data, $partitionValue = null): int
    {
        $data = $this->filterData($data);

        // 分表处理 - 优先使用外部传入的分表依据
        if ($this->isPartitionEnabled() && $partitionValue) {
            $this->setTableForPartition($partitionValue);
        }

        $model = $this->newInstance($data);
        $model->save();
        return $model->id;
    }

    /**
     * 修改记录（支持分表）
     * @param int $id
     * @param array $attributes
     * @param mixed $partitionValue 分表依据值
     * @return int
     */
    public function modify(int $id, array $attributes, $partitionValue = null): int
    {
        $attributes = $this->filterData($attributes);

        if ($this->isPartitionEnabled() && $partitionValue) {
            $this->setTableForPartition($partitionValue);
        }

        return $this->where('id', $id)->update($attributes);
    }

    /**
     * 条件更新（支持分表）
     * @param array $params 更新条件
     * @param array $attributes 更新数据
     * @param mixed $partitionValue 分表依据值
     * @return int
     */
    public function updateBy(array $params, array $attributes, $partitionValue = null): int
    {
        $attributes = $this->filterData($attributes);

        if ($this->isPartitionEnabled() && $partitionValue) {
            $this->setTableForPartition($partitionValue);
        }

        return $this->buildQuery($params)->update($attributes);
    }

    /**
     * 删除记录（支持分表）
     * @param int $id
     * @param mixed $partitionValue 分表依据值
     * @return int
     */
    public function remove(int $id, $partitionValue = null): int
    {
        if ($this->isPartitionEnabled() && $partitionValue) {
            $this->setTableForPartition($partitionValue);
        }

        return $this->where('id', $id)->delete();
    }

    /**
     * 硬删除记录（支持分表）
     * @param int $id
     * @param mixed $partitionValue 分表依据值
     * @return int
     */
    public function erase(int $id, $partitionValue = null): int
    {
        if ($this->isPartitionEnabled() && $partitionValue) {
            $this->setTableForPartition($partitionValue);
        }

        return $this->where('id', $id)->forceDelete();
    }

    /**
     * 条件删除（支持分表）
     * @param array $params
     * @param mixed $partitionValue 分表依据值
     * @return int
     */
    public function clear(array $params = [], $partitionValue = null): int
    {
        if ($this->isPartitionEnabled() && $partitionValue) {
            $this->setTableForPartition($partitionValue);
        }

        return $this->buildQuery($params)->delete();
    }

    /**
     * 条件硬删除（支持分表）
     * @param array $params
     * @param mixed $partitionValue 分表依据值
     * @return int
     */
    public function purge(array $params = [], $partitionValue = null): int
    {
        if ($this->isPartitionEnabled() && $partitionValue) {
            $this->setTableForPartition($partitionValue);
        }

        return $this->buildQuery($params)->forceDelete();
    }

    /**
     * 获取条数（支持分表）
     * @param array $params
     * @param mixed $partitionValue 分表依据值
     * @return int
     */
    public function total(array $params = [], $partitionValue = null): int
    {
        if ($this->isPartitionEnabled() && $partitionValue) {
            $this->setTableForPartition($partitionValue);
        }

        return $this->buildQuery($params)->count();
    }
}
